Go 模拟一个惰性生成器
编写一个惰性生成器
这里先尝试编写一个简单的惰性生成器
import (
"fmt"
)
var resume chan int
func integers() chan int {
yield := make(chan int)
count := 0
go func() {
for {
yield <- count
count++
}
}()
return yield
}
func generateInteger() int {
return <-resume
}
func main() {
resume = integers()
fmt.Println(generateInteger()) //=> 0
fmt.Println(generateInteger()) //=> 1
fmt.Println(generateInteger()) //=> 2
}
编写一个通用的惰性生产器
import (
"fmt"
)
type Any interface{}
type EvalFunc func(Any) (Any, Any)
func main() {
evenFunc := func(state Any) (Any, Any) {
os := state.(int)
ns := os + 2
return os, ns
}
even := BuildLazyIntEvaluator(evenFunc, 0)
// 模拟调用 10 次
for i := 0; i < 10; i++ {
fmt.Printf("%vth even: %v\n", i, even())
}
}
// BuildLazyEvaluator 通用的惰性生产器的工厂函数
func BuildLazyEvaluator(evalFunc EvalFunc, initState Any) func() Any {
retValChan := make(chan Any)
loopFunc := func() {
var actState Any = initState
var retVal Any
for {
retVal, actState = evalFunc(actState)
retValChan <- retVal
}
}
retFunc := func() Any {
return <-retValChan
}
go loopFunc()
return retFunc
}
func BuildLazyIntEvaluator(evalFunc EvalFunc, initState Any) func() int {
ef := BuildLazyEvaluator(evalFunc, initState)
return func() int {
return ef().(int)
}
}
输出:
0th even: 0
1th even: 2
2th even: 4
3th even: 6
4th even: 8
5th even: 10
6th even: 12
7th even: 14
8th even: 16
9th even: 18
模拟一个 yield
TODO: 待学习...
Python、ES6 等语言都有这个生成器 generator 和 yield,所以它们到底有什么用?
其实回顾学习 Unity 时就可以看到 Unity 通过这个生成器来实现异步操作,实际它就是在一个函数里面保存状态,每次调用只执行一部分
例如
function* Gen() {
let curr = 0
while (true) {
yield curr++;
}
}
每次调用这个 Gen()
只会得到当前的值
或者像 Unity 的协程那样使用,把一件事分开多步执行
function* Gen() {
// do something....
yield return
// do something....
yield return
// do something....
yield return
}
补充:实际上,这个 Gen() 就是生成了一个迭代器,包括 php 在内的大多数语言实现迭代器的方式,是通过一个可中断的函数完成的。而协程的一个特点就是执行中断,切换上下文。所以就有了通过迭代器函数去实现协程的方案。这种方案充分利用了迭代器可中断的特点来模拟协程中断,而利用闭包函数的上下文独立性,实现协程的上下文切换。